/*
 * @Author: wuqingchun
 * @Date: 2024-10-16 17:01:49
 * @Description: 
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-10-31 20:51:57
 */

#include <iostream>
#include <cstring>
#include <cerrno>

#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>

#include <sys/select.h>
#include <sys/time.h>
#include <sys/prctl.h>

#include "libnet_error.h"
#include "net_tcp_client.h"
#include "net_event_select.h"


CNetTCPClient::CNetTCPClient(INetEventHandler& refEvHandler, INetTCPConnectionCallback& refCallback, const sockaddr_in& refAddr):
    m_refEvHandler(refEvHandler),
    m_refCallback(refCallback),
    m_stAddr(refAddr)
{
    FD_ZERO(&m_stOriginReadSet);
    FD_ZERO(&m_stOriginWriteSet);
}

CNetTCPClient::~CNetTCPClient()
{
    if (m_iSockFd != -1)
    {
        (void)close(m_iSockFd);
        m_iSockFd = -1;
    }
}

int32_t CNetTCPClient::Start()
{
    int32_t iErr = m_objSndBuffer.Init(4096);
    if (iErr != ERROR_SUCCESS)
    {
        return iErr;
    }

    m_enState = CLI_STATE_INIT;
    m_bConnMgrRunning = true;
    m_objConnMgrThread = std::thread(&CNetTCPClient::__ConnMgrThreadFunc, this);

    return ERROR_SUCCESS;
}

void CNetTCPClient::Stop()
{
    m_bConnMgrRunning = false;

    if (m_objConnMgrThread.joinable())
    {
        m_objConnMgrThread.join();
    }
}

int32_t CNetTCPClient::SendMessage(const void* pData, size_t ulDataLen)
{
    // if (m_enState != CLI_STATE_CONNECTED)
    // {
    //     printf("Connection has not been established\n");
    //     return ERROR_FAILED;
    // }

    // return __Send(pData, ulDataLen);
}

int32_t CNetTCPClient::__CreateSocket()
{
    if (m_iSockFd != -1)
    {
        close(m_iSockFd);
        m_iSockFd = -1;
    }

    m_iSockFd = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP);
    if (m_iSockFd == -1)
    {
        printf("socket error, %s\n", strerror(errno));
        return ERROR_FAILED;
    }

    return ERROR_SUCCESS;
}

int32_t CNetTCPClient::__Connect()
{
    int32_t iErr = ERROR_SUCCESS;

    iErr = __CreateSocket();
    if (iErr != 0)
    {
        return iErr;
    }

    iErr = connect(m_iSockFd, (const sockaddr*)&m_stAddr, sizeof(m_stAddr));
    if (iErr == -1)
    {
        // connect 系统调用被信号打断之后，不能重试，否则会立即返回错误
        if (errno == EINTR || errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK)
        {
            // 非阻塞模式，连接还没有完成，视为成功
            iErr = ERROR_SUCCESS;
        }
        else
        {
            iErr = ERROR_FAILED;
            printf("connect error, ret=%d, errno=%d, errstr=%s\n", iErr, errno, strerror(errno));
        }
    }

    return iErr;
}

ssize_t CNetTCPClient::__RecvOne(void* pBuf, size_t ulBufLen)
{
    ssize_t lRecv =  0;
    bool bRetry = false;

    do
    {
        lRecv = recv(m_iSockFd, pBuf, ulBufLen, 0);
        if (lRecv == -1)
        {
            if (errno == EINTR)
            {
                // 被信号打断，需要重试
                bRetry = true;
                continue;
            }

            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                // 非阻塞模式，没有数据可以读
                lRecv = 0;
                break;
            }
        }
        else if (lRecv == 0)
        {
            // 对端执行了正常的连接关闭
            lRecv = -1;
        }
    } while (bRetry);

    return lRecv;
}

void CNetTCPClient::__Recv()
{
    char acBuf[4096];
    ssize_t lRecvLen = 0;

    do
    {
        lRecvLen = __RecvOne(acBuf, sizeof(acBuf));
        if (lRecvLen == -1)
        {
            m_enState = CLI_STATE_DISCONNECTED;
        }
        else if (lRecvLen > 0)
        {
            m_refCallback.OnMessage(acBuf, lRecvLen);
        }
    } while (lRecvLen == sizeof(acBuf));
}

ssize_t CNetTCPClient::__SendOne(const void* pData, size_t ulDataLen)
{
    bool bRetry = false;
    ssize_t lSendLen  = 0;

    do
    {
        lSendLen = send(m_iSockFd, pData, ulDataLen, MSG_NOSIGNAL);
        if (lSendLen == -1)
        {
            if (errno == EINTR)
            {
                // 被信号中断，重试
                bRetry = true;
                continue;
            }

            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                lSendLen = 0;
            }
        }
    } while (bRetry);

    return lSendLen;
}

int32_t CNetTCPClient::__Send(const void* pData, size_t ulDataLen)
{
    ssize_t lSendLen = 0;

    if (m_objSndBuffer.Empty())
    {
        lSendLen = __SendOne(pData, ulDataLen);
        if (lSendLen == -1)
        {
            // 连接错误
            printf("send error, errno=%d, errstr=%s\n", errno, strerror(errno));

            return ERROR_FAILED;
        }
    }
    
    if (lSendLen < ulDataLen)
    {
        printf("Send buffer is full\n");

        if (m_objSndBuffer.AddData((char*)pData + lSendLen, ulDataLen - lSendLen) == 0)
        {
            // 需要关注可写事件
            __AddWEvent();
        }
        else
        {
            // 数据丢失
            printf("TODO ....\n");
        }

        return ERROR_NOT_COMPLETE;
    }

    return ERROR_SUCCESS;
}


void CNetTCPClient::__SendBuffer()
{
    BUFFER_ITEM_S stItem;
    ssize_t lSendLen = 0;
    bool bDeleteWEvent = true;

    while (m_objSndBuffer.GetItem(stItem))
    {
        lSendLen = __SendOne(stItem.pcData, stItem.ulDataLen);
        if (lSendLen == -1)
        {
            m_objSndBuffer.PushBack(stItem);
            bDeleteWEvent = false;
            break;
        }

        if (lSendLen < stItem.ulDataLen)
        {
            stItem.pcData = stItem.pcData + lSendLen;
            stItem.ulDataLen -= lSendLen;
            m_objSndBuffer.PushBack(stItem);
            bDeleteWEvent = false;
            break;
        }

        m_objSndBuffer.Free(stItem);
    }

    if (bDeleteWEvent)
    {
        __DeleteWEvent();
    }
}

int32_t CNetTCPClient::__WaitEvent()
{
    int iFdNum;

    timeval stTimeout = m_stWaitTimeout;

    do
    {
        m_stTmpReadSet = m_stOriginReadSet;
        m_stTmpWriteSet = m_stOriginWriteSet;
        
        iFdNum = select(m_iSockFd + 1, &m_stTmpReadSet, &m_stTmpWriteSet, NULL, &stTimeout);
    } while (errno == EINTR);

    if (iFdNum == -1)
    {
        perror("select error");

        return ERROR_FAILED;
    }

    if (iFdNum > 0)
    {
        if (m_enState != CLI_STATE_CONNECTING)
        {
            __DealREvent();
        }

        __DealWEvent();
    }

    return ERROR_SUCCESS;
}

void CNetTCPClient::__AddWEvent()
{
    FD_SET(m_iSockFd, &m_stOriginWriteSet);
}

void CNetTCPClient::__DeleteWEvent()
{
    FD_CLR(m_iSockFd, &m_stOriginWriteSet);
}

void CNetTCPClient::__AddREvent()
{
    FD_SET(m_iSockFd, &m_stOriginReadSet);
}

void CNetTCPClient::__DeleteREvent()
{
    FD_CLR(m_iSockFd, &m_stOriginReadSet);
}

void CNetTCPClient::__DealWEvent()
{
    if (FD_ISSET(m_iSockFd, &m_stOriginWriteSet))
    {
        int iErr = 0;
        socklen_t uiLen = sizeof(iErr);

        if (m_enState == CLI_STATE_CONNECTING)
        {
            if (getsockopt(m_iSockFd, SOL_SOCKET, SO_ERROR, &iErr, &uiLen) < 0 || iErr != 0)
            {
                m_enState = CLI_STATE_INIT;
                // 连接失败，需要重试
                printf("Failed to connect server %s:%hu\n", inet_ntoa(m_stAddr.sin_addr), htons(m_stAddr.sin_port));
                std::this_thread::sleep_for(std::chrono::seconds(1));
            }
            else
            {
                // 关注可读事件
                __AddREvent();
                
                // 取消关注可写事件
                __DeleteWEvent();

                m_enState = CLI_STATE_CONNECTED;
            }
        }
        else
        {
            __SendBuffer();
        }
    }
}

void CNetTCPClient::__DealREvent()
{
    if (FD_ISSET(m_iSockFd, &m_stOriginReadSet))
    {
        // 接收数据
        __Recv();
    }
}

void CNetTCPClient::__ConnMgrThreadFunc()
{
    int32_t iErr;

    printf("Connection manager thread is running\n");

    prctl(PR_SET_NAME, "conn_mgr");

    while (m_bConnMgrRunning)
    {
        // if (m_enState == CLI_STATE_INIT || m_enState == CLI_STATE_DISCONNECTED)
        // {
        //     iErr = __Connect();
        //     if (iErr != 0)
        //     {
        //         // 连接失败，需要重试
        //         printf("Failed to connect server %s:%hu\n", inet_ntoa(m_stAddr.sin_addr), htons(m_stAddr.sin_port));
        //         std::this_thread::sleep_for(std::chrono::seconds(1));
        //         continue;
        //     }

        //     printf("Connecting ...\n");
        //     m_enState = CLI_STATE_CONNECTING;
        //     __AddWEvent();
        // }
        // else
        // {
        //     iErr = __WaitEvent();
        //     if (iErr == -1)
        //     {
        //         break;
        //     }
        // }

        m_refEvHandler.Wait();
    }

    printf("Connection manager thread will exit\n");
}


INetTCPClient* NewTCPClient(INetTCPConnectionCallback* pobjCallback, const char* pcHost, uint16_t usPort)
{
    static CNetEventSelect objSelect;
    
    if (pobjCallback== nullptr || pcHost == nullptr || *pcHost == '\0' || usPort == 0)
    {
        printf("Invalid parameter\n");
        return nullptr;
    }

    sockaddr_in stAddr{};

    if (inet_pton(AF_INET, pcHost, &stAddr.sin_addr) != 1)
    {
        printf("Invalid parameter\n");
        return nullptr;
    }

    stAddr.sin_family = AF_INET;
    stAddr.sin_port = htons(usPort);

    return new (std::nothrow) CNetTCPClient(objSelect, *pobjCallback, stAddr);
}

void DeleteTCPClient(INetTCPClient* pobjClient)
{
    if (pobjClient != nullptr)
    {
        delete (CNetTCPClient *)pobjClient;
    }
}